
import java.awt.image.BufferedImage;
import java.util.Calendar;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Synthesizer;

import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.Player;
import javax.media.Time;
import javax.media.control.FrameGrabbingControl;
import javax.media.format.VideoFormat;
import javax.media.util.BufferToImage;
import javax.sound.midi.MidiDevice.Info;
import javax.swing.JOptionPane;

public class LatencyTest {

    private Synthesizer synth;
    private MidiChannel channel;
    private boolean running = true;

    private static String locatorString = "vfw://0";
    private Player player;
    private MediaLocator locator;
    private FrameGrabbingControl grabber;
    private BufferToImage btoi;
    private volatile BufferedImage image;
    private volatile BufferedImage tempImage;

    public static void main(String[] args) {
        new LatencyTest();
    }

    public LatencyTest(){
        //Initialize the hardware:
        System.out.println("Initializing...");
        initCamera();
        initMIDI();

        //Create the background close-program option.
        Thread closeThread = new Thread(new Runnable(){
            public void run() {
                JOptionPane.showMessageDialog(null, "Click OK to cancel execution...", "Close", JOptionPane.INFORMATION_MESSAGE);
                running = false;
            }
        });
        Thread grabThread = new Thread(new Runnable(){
            public void run() {
                while(true){
                    if(running){
                        tempImage = (BufferedImage)btoi.createImage(grabber.grabFrame());
                        tempImage.setAccelerationPriority(1);
                    }
                }
            }
        });
        grabThread.setPriority(Thread.MAX_PRIORITY);
        grabThread.start();
        closeThread.start();
        System.out.println("Started...");
        Thread runThread = new Thread(new Runnable() {
            public void run() {
                detectWhite();
            }
        });
        runThread.setPriority(Thread.MAX_PRIORITY);
        runThread.start();
        while(running){
            try {
                Thread.sleep(500);
            } catch (InterruptedException ex) {
            }
        }
        shutdown();
        System.exit(0);
    }

    private void shutdown(){
        player.close();
        player.deallocate();
        channel.allSoundOff();
        synth.close();
    }

    private void detectWhite(){
        int w,h;
        boolean foundWhite = false;
        long start, got, stop;
        boolean on = false;
        long one, two;
        while(true){
            if(tempImage == null){
                continue;
            }
            if(!running){
                break;
            }
            start = Calendar.getInstance().getTimeInMillis();
            image = (BufferedImage)tempImage;
            image.setAccelerationPriority(1);
            got = Calendar.getInstance().getTimeInMillis();
            w = image.getWidth();
            h = image.getHeight();
            foundWhite = false;
            if((image.getRGB(w/2, h/2)&0x0ff) > 75){
                foundWhite = true;
                if(running && !on) {
                    one = Calendar.getInstance().getTimeInMillis();
                    channel.noteOn(60, 75);
                    two = Calendar.getInstance().getTimeInMillis();
                    System.out.println("Start:\n\t"+start+"\n\t"+got+"\n\t"+one+"\n\t"+two);
                    on = true;
                }
            }
            /*for(int i = 0; i < w; i++){
                for(int j = 0; j < h; j++){
                    if((image.getRGB(i, j)&0x0ff) > 127){
                        foundWhite = true;
                        if(running && !on){
                            System.out.println("Start:\n\t"+start+"\n\t"+got);
                            channel.noteOn(60, 75);
                            on = true;
                        }
                        break;
                    }
                }
                if(foundWhite){
                    break;
                }
            }*/
            stop = Calendar.getInstance().getTimeInMillis();
            
            if(!foundWhite && running && on){
                channel.allNotesOff();
                on = false;
                System.out.println("\t"+stop);
            }
        }
    }

    private void initCamera(){
        try{
            locator = new MediaLocator(locatorString);
            player = Manager.createRealizedPlayer(locator);
            player.start();
            Time wait = player.getStartLatency();
            Thread.sleep(wait.getNanoseconds()/1000);
            grabber = (FrameGrabbingControl) player.getControl("javax.media.control.FrameGrabbingControl");
            btoi = new BufferToImage((VideoFormat) grabber.grabFrame().getFormat());
        }
        catch(Exception e){
            JOptionPane.showMessageDialog(null, "Error occured while initializing the camera.\n"
                    +e.getMessage(), "CAMERA ERROR", JOptionPane.ERROR_MESSAGE);
        }
    }

    private void initMIDI(){
        try{
            Info[] infos = MidiSystem.getMidiDeviceInfo();
            System.out.println("Devices found:");
            for(int x = 0; x < infos.length; x++){
                System.out.println('\t'+infos[x].getName()+'\t'+infos[x].getVendor());
            }
            System.exit(0);
            synth = MidiSystem.getSynthesizer();
            synth.open();
            channel = synth.getChannels()[0];
            channel.programChange(81);
        }
        catch(MidiUnavailableException e){
            JOptionPane.showMessageDialog(null, "Midi Error:\n"+e.getMessage(), "MIDI ERROR", JOptionPane.ERROR_MESSAGE);
        }
    }
}
